/**
 * tiny_jpeg.h
 * Tiny JPEG 编码器
 *  - Sergio Gonzalez
 * 
 * 这是一个可读性强且简单的单头文件JPEG编码器实现。
 * 功能特点：
 *  - 实现了基于DCT的基线JPEG压缩
 *  - 无动态内存分配
 * 
 * 该库遵循stb库的设计理念，并大多遵循stb的指导原则。
 * 使用C99编写，依赖C标准库，兼容C++11
 * 
 * ==== 致谢 ====
 * AssociationSirius (Bug报告)
 * Bernard van Gastel (线程安全的默认值，BSD编译)
 * 
 * ==== 许可证 ====
 * 本软件属于公共领域。在未被承认的情况下，您被授予永久性、不可撤销的许可证，
 * 可以随意复制和修改本文件。
 */

// 使用示例
// 必须在某一个C文件中定义TJE_IMPLEMENTATION并包含此头文件
// #define TJE_IMPLEMENTATION
// #include "tiny_jpeg.h"

// 防止C++编译器对C代码进行名称修饰
#ifdef __cplusplus
extern "C" // 使用C链接约定
{
#endif



// GCC和Clang编译器特定指令
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push                                   // 保存当前诊断状态
#pragma GCC diagnostic ignored "-Wmissing-field-initializers" // 忽略缺少字段初始化警告
#pragma GCC diagnostic ignored "-Wmissing-braces"             // 忽略缺少大括号警告
#pragma GCC diagnostic ignored "-Wpadded"                     // 忽略填充警告
#endif

// ============================================================
// 公共接口
// ============================================================

// 头文件保护宏
#ifndef TJE_HEADER_GUARD
#define TJE_HEADER_GUARD

// - tje_encode_to_file -
// 功能：将位图数据编码为JPEG并写入文件
// 参数：
//      dest_path: 输出文件名（如"out.jpg"）
//      width, height: 图像尺寸（像素）
//      num_components: 颜色分量数（3为RGB，4为RGBA）
//      src_data: 指向像素数据的指针
// 返回值：0表示错误，1表示成功
int tje_encode_to_file(const char *dest_path,
                       const int width,
                       const int height,
                       const int num_components,
                       const unsigned char *src_data);

// - tje_encode_to_file_at_quality -
// 功能：以指定质量将位图数据编码为JPEG并写入文件
// 参数：
//      dest_path: 输出文件名
//      quality: 质量等级（3:最高质量，压缩比变化大；2:高质量，约为3的一半大小；
//                1:可察觉质量损失，约为3的1/6大小或2的1/3大小）
//      width, height: 图像尺寸
//      num_components: 颜色分量数
//      src_data: 指向像素数据的指针
// 返回值：0表示错误，1表示成功
int tje_encode_to_file_at_quality(const char *dest_path,
                                  const int quality,
                                  const int width,
                                  const int height,
                                  const int num_components,
                                  const unsigned char *src_data);

// 写入函数指针类型定义
typedef void tje_write_func(void *context, void *data, int size);

// - tje_encode_with_func -
// 功能：使用自定义写入函数编码JPEG
// 参数：
//      func: 自定义写入函数
//      context: 传递给写入函数的上下文
//      quality: 质量等级
//      width, height: 图像尺寸
//      num_components: 颜色分量数
//      src_data: 指向像素数据的指针
// 返回值：0表示错误，1表示成功
int tje_encode_with_func(tje_write_func *func,
                         void *context,
                         const int quality,
                         const int width,
                         const int height,
                         const int num_components,
                         const unsigned char *src_data);

#endif // TJE_HEADER_GUARD

// ============================================================
// 实现部分
// ============================================================
// 在应用程序的一个源文件中定义TJE_IMPLEMENTATION并包含此头文件
#ifdef TJE_IMPLEMENTATION

// 最小值和最大值宏
#define tjei_min(a, b) ((a) < b) ? (a) : (b);
#define tjei_max(a, b) ((a) < b) ? (b) : (a);

// 强制内联指令
#if defined(_MSC_VER) // Microsoft Visual C++
#define TJEI_FORCE_INLINE __forceinline
#else // GCC和Clang
#define TJEI_FORCE_INLINE static
#endif

// 是否使用快速DCT
#define TJE_USE_FAST_DCT 1

// 包含必要的C标准库头文件
#include <assert.h>   // 断言
#include <inttypes.h> // 整数类型
#include <math.h>     // 数学函数
#include <stdio.h>    // 文件操作
#include <string.h>   // 内存操作

// 输出缓冲区大小
#define TJEI_BUFFER_SIZE 1024

// Windows平台特定定义
#ifdef _WIN32
#include <windows.h> // Windows API
#ifndef snprintf
#define snprintf sprintf_s // 安全版本snprintf
#endif
#endif

// 调试日志宏
#ifndef NDEBUG // 调试模式
#ifdef _WIN32
#define tje_log(msg) OutputDebugStringA(msg) // Windows调试输出
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define tje_log(msg) puts(msg) // Unix-like平台输出到控制台
#else
#warning "需要为您的平台定义tje_log以用于调试目的（使用NDEBUG编译时不需要）"
#endif
#else                // 发布模式
#define tje_log(msg) // 空定义
#endif               // NDEBUG

// 写入上下文结构
typedef struct
{
    void *context;        // 用户提供的上下文
    tje_write_func *func; // 写入函数指针
} TJEWriteContext;

// JPEG编码状态结构
typedef struct
{
    // 哈夫曼数据
    uint8_t ehuffsize[4][257];  // 扩展哈夫曼大小表
    uint16_t ehuffcode[4][256]; // 扩展哈夫曼编码表
    uint8_t const *ht_bits[4];  // 哈夫曼位数表
    uint8_t const *ht_vals[4];  // 哈夫曼值表

    // 量化表
    uint8_t qt_luma[64];   // 亮度量化表
    uint8_t qt_chroma[64]; // 色度量化表

    // 写入上下文（默认使用fwrite，使用tje_encode_with_func时为用户自定义）
    TJEWriteContext write_context;

    // 缓冲输出（使用标准库实现时大幅提升性能）
    size_t output_buffer_count;              // 缓冲区当前数据量
    uint8_t output_buffer[TJEI_BUFFER_SIZE]; // 输出缓冲区
} TJEState;

// ============================================================
// 表定义
// 规范定义了相当好的默认量化矩阵和哈夫曼规范表
// ============================================================

// K.1 - 建议的亮度量化表
static const uint8_t tjei_default_qt_luma_from_spec[] = {
    16,
    11,
    10,
    16,
    24,
    40,
    51,
    61,
    12,
    12,
    14,
    19,
    26,
    58,
    60,
    55,
    14,
    13,
    16,
    24,
    40,
    57,
    69,
    56,
    14,
    17,
    22,
    29,
    51,
    87,
    80,
    62,
    18,
    22,
    37,
    56,
    68,
    109,
    103,
    77,
    24,
    35,
    55,
    64,
    81,
    104,
    113,
    92,
    49,
    64,
    78,
    87,
    103,
    121,
    120,
    101,
    72,
    92,
    95,
    98,
    112,
    100,
    103,
    99,
};

// 未使用的色度量化表
#if 0
static const uint8_t tjei_default_qt_chroma_from_spec[] =
{
    // K.1 - 建议的色度量化表
   17,18,24,47,99,99,99,99,
   18,21,26,66,99,99,99,99,
   24,26,56,99,99,99,99,99,
   47,66,99,99,99,99,99,99,
   99,99,99,99,99,99,99,99,
   99,99,99,99,99,99,99,99,
   99,99,99,99,99,99,99,99,
   99,99,99,99,99,99,99,99,
};
#endif

// 来自论文的示例色度量化表
static const uint8_t tjei_default_qt_chroma_from_paper[] = {
    16,
    12,
    14,
    14,
    18,
    24,
    49,
    72,
    11,
    10,
    16,
    24,
    40,
    51,
    61,
    12,
    13,
    17,
    22,
    35,
    64,
    92,
    14,
    16,
    22,
    37,
    55,
    78,
    95,
    19,
    24,
    29,
    56,
    64,
    87,
    98,
    26,
    40,
    51,
    68,
    81,
    103,
    112,
    58,
    57,
    87,
    109,
    104,
    121,
    100,
    60,
    69,
    80,
    103,
    113,
    120,
    103,
    55,
    56,
    62,
    77,
    92,
    101,
    99,
};

// == 哈夫曼树"压缩"过程：JPEG规范C.2

// 每个代码长度的16位值数量（K.3.3.1）
static const uint8_t tjei_default_ht_luma_dc_len[16] = {
    0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
};
// 值
static const uint8_t tjei_default_ht_luma_dc[12] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
};

// 每个代码长度的16位值数量（K.3.3.1）
static const uint8_t tjei_default_ht_chroma_dc_len[16] = {
    0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
};
// 值
static const uint8_t tjei_default_ht_chroma_dc[12] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
};

// AC系数（同上）
static const uint8_t tjei_default_ht_luma_ac_len[16] = {
    0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
};
static const uint8_t tjei_default_ht_luma_ac[] = {
    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
    0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
    0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
    0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
    0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
    0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
    0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
    0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
    0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
    0xF9, 0xFA
};

static const uint8_t tjei_default_ht_chroma_ac_len[16] = {
    0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
};
static const uint8_t tjei_default_ht_chroma_ac[] = {
    0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
    0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
    0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
    0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
    0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
    0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
    0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
    0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
    0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
    0xF9, 0xFA
};

// ============================================================
// 代码实现
// ============================================================

// Zig-zag顺序表
static const uint8_t tjei_zig_zag[64] = {
    0,
    1,
    5,
    6,
    14,
    15,
    27,
    28,
    2,
    4,
    7,
    13,
    16,
    26,
    29,
    42,
    3,
    8,
    12,
    17,
    25,
    30,
    41,
    43,
    9,
    11,
    18,
    24,
    31,
    40,
    44,
    53,
    10,
    19,
    23,
    32,
    39,
    45,
    52,
    54,
    20,
    22,
    33,
    38,
    46,
    51,
    55,
    60,
    21,
    34,
    37,
    47,
    50,
    56,
    59,
    61,
    35,
    36,
    48,
    49,
    57,
    58,
    62,
    63,
};

// 将本地字节序转换为大端序
static uint16_t tjei_be_word(const uint16_t native_word)
{
    uint8_t bytes[2];
    uint16_t result;
    bytes[1] = (native_word & 0x00ff);        // 低字节
    bytes[0] = ((native_word & 0xff00) >> 8); // 高字节
    memcpy(&result, bytes, sizeof(bytes));    // 复制到结果
    return result;
}

// ============================================================
// 以下结构体仅用于代码清晰度、可调试性和可读性
// 它们在写入磁盘时使用，但对于记录格式工作原理和在开发时检查内存非常有用
// ============================================================

static const uint8_t tjeik_jfif_id[] = "JFIF";                         // JFIF标识
static const uint8_t tjeik_com_str[] = "Created by Tiny JPEG Encoder"; // 注释字符串

// 打包结构体（确保内存布局与JPEG格式完全一致）
#pragma pack(push) // 保存当前对齐方式
#pragma pack(1)    // 设置为1字节对齐
// JPEG文件头结构
typedef struct
{
    uint16_t SOI; // 起始标识(0xFFD8)
    // JFIF头
    uint16_t APP0;      // 应用标记(0xFFE0)
    uint16_t jfif_len;  // JFIF段长度
    uint8_t jfif_id[5]; // JFIF标识符"JFIF"
    uint16_t version;   // JFIF版本
    uint8_t units;      // 单位
    uint16_t x_density; // X方向密度
    uint16_t y_density; // Y方向密度
    uint8_t x_thumb;    // 缩略图宽度
    uint8_t y_thumb;    // 缩略图高度
} TJEJPEGHeader;

// JPEG注释结构
typedef struct
{
    uint16_t com;                            // 注释标记(0xFFFE)
    uint16_t com_len;                        // 注释长度
    char com_str[sizeof(tjeik_com_str) - 1]; // 注释内容
} TJEJPEGComment;

// 帧组件规格辅助结构
typedef struct
{
    uint8_t component_id;     // 组件ID
    uint8_t sampling_factors; // 采样因子（高4位：水平，低4位：垂直）
    uint8_t qt;               // 量化表选择器
} TJEComponentSpec;

// 帧头结构
typedef struct
{
    uint16_t SOF;                       // 帧开始标记(0xFFC0)
    uint16_t len;                       // 帧长度(8 + 3 * 组件数)
    uint8_t precision;                  // 样本精度（每样本位数）
    uint16_t height;                    // 图像高度
    uint16_t width;                     // 图像宽度
    uint8_t num_components;             // 组件数（本实现中为3）
    TJEComponentSpec component_spec[3]; // 组件规格
} TJEFrameHeader;

// 扫描组件规格结构
typedef struct
{
    uint8_t component_id; // 组件ID
    uint8_t dc_ac;        // DC/AC表选择器
} TJEFrameComponentSpec;

// 扫描头结构
typedef struct
{
    uint16_t SOS;                            // 扫描开始标记(0xFFDA)
    uint16_t len;                            // 扫描长度
    uint8_t num_components;                  // 组件数（3）
    TJEFrameComponentSpec component_spec[3]; // 扫描组件规格
    uint8_t first;                           // 第一个DCT系数(0)
    uint8_t last;                            // 最后一个DCT系数(63)
    uint8_t ah_al;                           // 渐进参数(0)
} TJEScanHeader;
#pragma pack(pop) // 恢复之前的内存对齐方式

// 写入数据到状态缓冲区
static void tjei_write(TJEState *state, const void *data, size_t num_bytes, size_t num_elements)
{
    size_t to_write = num_bytes * num_elements; // 需要写入的总字节数

    // 限制到缓冲区可用大小并复制内存
    size_t capped_count = tjei_min(to_write, TJEI_BUFFER_SIZE - 1 - state->output_buffer_count);

    memcpy(state->output_buffer + state->output_buffer_count, data, capped_count);
    state->output_buffer_count += capped_count;

    assert(state->output_buffer_count <= TJEI_BUFFER_SIZE - 1);

    // 刷新缓冲区
    if (state->output_buffer_count == TJEI_BUFFER_SIZE - 1) {
        state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count);
        state->output_buffer_count = 0;
    }

    // 递归调用自身处理剩余数据
    if (capped_count < to_write) {
        tjei_write(state, (uint8_t *)data + capped_count, to_write - capped_count, 1);
    }
}

// 写入量化表(DQT)
static void tjei_write_DQT(TJEState *state, const uint8_t *matrix, uint8_t id)
{
    uint16_t DQT = tjei_be_word(0xffdb); // 量化表标记
    tjei_write(state, &DQT, sizeof(uint16_t), 1);
    uint16_t len = tjei_be_word(0x0043); // 长度(2+1+64=67=0x43)
    tjei_write(state, &len, sizeof(uint16_t), 1);
    assert(id < 4);
    uint8_t precision_and_id = id; // 精度(高4位)和ID(低4位)
    tjei_write(state, &precision_and_id, sizeof(uint8_t), 1);
    // 写入量化矩阵
    tjei_write(state, matrix, 64 * sizeof(uint8_t), 1);
}

// 哈夫曼表类型枚举
typedef enum {
    TJEI_DC = 0, // DC系数表
    TJEI_AC = 1  // AC系数表
} TJEHuffmanTableClass;

// 写入哈夫曼表(DHT)
static void tjei_write_DHT(TJEState *state,
                           uint8_t const *matrix_len,
                           uint8_t const *matrix_val,
                           TJEHuffmanTableClass ht_class,
                           uint8_t id)
{
    int num_values = 0;
    // 计算总数值数量
    for (int i = 0; i < 16; ++i) {
        num_values += matrix_len[i];
    }
    assert(num_values <= 0xffff);

    uint16_t DHT = tjei_be_word(0xffc4); // 哈夫曼表标记
    // 长度(2+1+16+数值数量)
    uint16_t len = tjei_be_word(2 + 1 + 16 + (uint16_t)num_values);
    assert(id < 4);
    uint8_t tc_th = (uint8_t)((((uint8_t)ht_class) << 4) | id); // 表类别和ID

    tjei_write(state, &DHT, sizeof(uint16_t), 1);
    tjei_write(state, &len, sizeof(uint16_t), 1);
    tjei_write(state, &tc_th, sizeof(uint8_t), 1);
    tjei_write(state, matrix_len, sizeof(uint8_t), 16);                 // 写入长度表
    tjei_write(state, matrix_val, sizeof(uint8_t), (size_t)num_values); // 写入值表
}

// ============================================================
// 哈夫曼压缩代码
// ============================================================

// 从BITS规范返回所有代码长度（JPEG C.3）
static uint8_t *tjei_huff_get_code_lengths(uint8_t huffsize[/*256*/], uint8_t const *bits)
{
    int k = 0;
    for (int i = 0; i < 16; ++i) {
        for (int j = 0; j < bits[i]; ++j) {
            huffsize[k++] = (uint8_t)(i + 1); // 代码长度
        }
        huffsize[k] = 0;
    }
    return huffsize;
}

// 填充每个代码的前缀
static uint16_t *tjei_huff_get_codes(uint16_t codes[], uint8_t *huffsize, int64_t count)
{
    uint16_t code = 0;
    int k = 0;
    uint8_t sz = huffsize[0];
    for (;;) {
        do {
            assert(k < count);
            codes[k++] = code++; // 分配代码
        } while (huffsize[k] == sz);
        if (huffsize[k] == 0) {
            return codes;
        }
        do {
            code = (uint16_t)(code << 1); // 左移一位
            ++sz;
        } while (huffsize[k] != sz);
    }
}

// 获取扩展哈夫曼表
static void tjei_huff_get_extended(uint8_t *out_ehuffsize,
                                   uint16_t *out_ehuffcode,
                                   uint8_t const *huffval,
                                   uint8_t *huffsize,
                                   uint16_t *huffcode, int64_t count)
{
    int k = 0;
    do {
        uint8_t val = huffval[k];
        out_ehuffcode[val] = huffcode[k]; // 扩展哈夫曼代码
        out_ehuffsize[val] = huffsize[k]; // 扩展哈夫曼大小
        k++;
    } while (k < count);
}

// ============================================================

// 返回：
//  out[1]: 位数
//  out[0]: 位值
TJEI_FORCE_INLINE void tjei_calculate_variable_length_int(int value, uint16_t out[2])
{
    int abs_val = value;
    if (value < 0) {
        abs_val = -abs_val;
        --value;
    }
    out[1] = 1;
    while (abs_val >>= 1) {
        ++out[1]; // 计算需要的位数
    }
    out[0] = (uint16_t)(value & ((1 << out[1]) - 1)); // 位值
}

// 向文件写入位
TJEI_FORCE_INLINE void tjei_write_bits(TJEState *state,
                                       uint32_t *bitbuffer, uint32_t *location,
                                       uint16_t num_bits, uint16_t bits)
{
    // 将位推入位缓冲区并保存位置
    uint32_t nloc = *location + num_bits;
    *bitbuffer |= (uint32_t)(bits << (32 - nloc)); // 将位放入缓冲区
    *location = nloc;
    while (*location >= 8) {
        // 获取最高有效字节
        uint8_t c = (uint8_t)((*bitbuffer) >> 24);
        // 写入文件
        tjei_write(state, &c, 1, 1);
        if (c == 0xff) {
            // 特殊情况：告诉JPEG这不是标记
            char z = 0;
            tjei_write(state, &z, 1, 1);
        }
        // 弹出栈
        *bitbuffer <<= 8;
        *location -= 8;
    }
}

// Thomas G. Lane的DCT实现
// 通过NVIDIA获取
// 基于Arai, Agui和Nakajima的缩放DCT算法
static void tjei_fdct(float *data)
{
    float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
    float tmp10, tmp11, tmp12, tmp13;
    float z1, z2, z3, z4, z5, z11, z13;
    float *dataptr;
    int ctr;

    // 第一遍：处理行
    dataptr = data;
    for (ctr = 7; ctr >= 0; ctr--) {
        tmp0 = dataptr[0] + dataptr[7];
        tmp7 = dataptr[0] - dataptr[7];
        tmp1 = dataptr[1] + dataptr[6];
        tmp6 = dataptr[1] - dataptr[6];
        tmp2 = dataptr[2] + dataptr[5];
        tmp5 = dataptr[2] - dataptr[5];
        tmp3 = dataptr[3] + dataptr[4];
        tmp4 = dataptr[3] - dataptr[4];

        // 偶数部分
        tmp10 = tmp0 + tmp3; // 阶段2
        tmp13 = tmp0 - tmp3;
        tmp11 = tmp1 + tmp2;
        tmp12 = tmp1 - tmp2;

        dataptr[0] = tmp10 + tmp11; // 阶段3
        dataptr[4] = tmp10 - tmp11;

        z1 = (tmp12 + tmp13) * ((float)0.707106781); // c4
        dataptr[2] = tmp13 + z1;                     // 阶段5
        dataptr[6] = tmp13 - z1;

        // 奇数部分
        tmp10 = tmp4 + tmp5; // 阶段2
        tmp11 = tmp5 + tmp6;
        tmp12 = tmp6 + tmp7;

        // 旋转器修改自图4-8以避免额外取反
        z5 = (tmp10 - tmp12) * ((float)0.382683433); // c6
        z2 = ((float)0.541196100) * tmp10 + z5;      // c2-c6
        z4 = ((float)1.306562965) * tmp12 + z5;      // c2+c6
        z3 = tmp11 * ((float)0.707106781);           // c4

        z11 = tmp7 + z3; // 阶段5
        z13 = tmp7 - z3;

        dataptr[5] = z13 + z2; // 阶段6
        dataptr[3] = z13 - z2;
        dataptr[1] = z11 + z4;
        dataptr[7] = z11 - z4;

        dataptr += 8; // 前进到下一行
    }

    // 第二遍：处理列
    dataptr = data;
    for (ctr = 8 - 1; ctr >= 0; ctr--) {
        tmp0 = dataptr[8 * 0] + dataptr[8 * 7];
        tmp7 = dataptr[8 * 0] - dataptr[8 * 7];
        tmp1 = dataptr[8 * 1] + dataptr[8 * 6];
        tmp6 = dataptr[8 * 1] - dataptr[8 * 6];
        tmp2 = dataptr[8 * 2] + dataptr[8 * 5];
        tmp5 = dataptr[8 * 2] - dataptr[8 * 5];
        tmp3 = dataptr[8 * 3] + dataptr[8 * 4];
        tmp4 = dataptr[8 * 3] - dataptr[8 * 4];

        // 偶数部分
        tmp10 = tmp0 + tmp3; // 阶段2
        tmp13 = tmp0 - tmp3;
        tmp11 = tmp1 + tmp2;
        tmp12 = tmp1 - tmp2;

        dataptr[8 * 0] = tmp10 + tmp11; // 阶段3
        dataptr[8 * 4] = tmp10 - tmp11;

        z1 = (tmp12 + tmp13) * ((float)0.707106781); // c4
        dataptr[8 * 2] = tmp13 + z1;                 // 阶段5
        dataptr[8 * 6] = tmp13 - z1;

        // 奇数部分
        tmp10 = tmp4 + tmp5; // 阶段2
        tmp11 = tmp5 + tmp6;
        tmp12 = tmp6 + tmp7;

        // 旋转器修改自图4-8以避免额外取反
        z5 = (tmp10 - tmp12) * ((float)0.382683433); // c6
        z2 = ((float)0.541196100) * tmp10 + z5;      // c2-c6
        z4 = ((float)1.306562965) * tmp12 + z5;      // c2+c6
        z3 = tmp11 * ((float)0.707106781);           // c4

        z11 = tmp7 + z3; // 阶段5
        z13 = tmp7 - z3;

        dataptr[8 * 5] = z13 + z2; // 阶段6
        dataptr[8 * 3] = z13 - z2;
        dataptr[8 * 1] = z11 + z4;
        dataptr[8 * 7] = z11 - z4;

        dataptr++; // 前进到下一列
    }
}

// 如果不使用快速DCT，使用慢速DCT实现
#if !TJE_USE_FAST_DCT
static float slow_fdct(int u, int v, float *data)
{
#define kPI 3.14159265f
    float res = 0.0f;
    float cu = (u == 0) ? 0.70710678118654f : 1;
    float cv = (v == 0) ? 0.70710678118654f : 1;
    for (int y = 0; y < 8; ++y) {
        for (int x = 0; x < 8; ++x) {
            res += (data[y * 8 + x]) *
                   cosf(((2.0f * x + 1.0f) * u * kPI) / 16.0f) *
                   cosf(((2.0f * y + 1.0f) * v * kPI) / 16.0f);
        }
    }
    res *= 0.25f * cu * cv;
    return res;
#undef kPI
}
#endif

// 绝对值宏
#define ABS(x) ((x) < 0 ? -(x) : (x))

// 编码并写入MCU（最小编码单元）
static void tjei_encode_and_write_MCU(TJEState *state,
                                      float *mcu,
#if TJE_USE_FAST_DCT
                                      float *qt, // 预处理的量化矩阵
#else
                                      uint8_t *qt,
#endif
                                      uint8_t *huff_dc_len, uint16_t *huff_dc_code, // 哈夫曼表
                                      uint8_t *huff_ac_len, uint16_t *huff_ac_code,
                                      int *pred,           // 先前的DC系数
                                      uint32_t *bitbuffer, // 位堆栈
                                      uint32_t *location)
{
    int du[64]; // Zig-zag顺序的数据单元

    float dct_mcu[64];
    memcpy(dct_mcu, mcu, 64 * sizeof(float)); // 复制MCU数据

#if TJE_USE_FAST_DCT
    tjei_fdct(dct_mcu); // 执行快速DCT
    for (int i = 0; i < 64; ++i) {
        float fval = dct_mcu[i];
        fval *= qt[i]; // 量化
#if 0
        fval = (fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f);  // 四舍五入
#else
        fval = floorf(fval + 1024 + 0.5f); // 带偏移的四舍五入
        fval -= 1024;
#endif
        int val = (int)fval;
        du[tjei_zig_zag[i]] = val; // 按Zig-zag顺序存储
    }
#else
    // 慢速DCT实现
    for (int v = 0; v < 8; ++v) {
        for (int u = 0; u < 8; ++u) {
            dct_mcu[v * 8 + u] = slow_fdct(u, v, mcu);
        }
    }
    for (int i = 0; i < 64; ++i) {
        float fval = dct_mcu[i] / (qt[i]);                                      // 量化
        int val = (int)((fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f)); // 四舍五入
        du[tjei_zig_zag[i]] = val;                                              // 按Zig-zag顺序存储
    }
#endif

    uint16_t vli[2]; // 变长整数

    // 编码DC系数
    int diff = du[0] - *pred; // 与前一个DC系数的差值
    *pred = du[0];
    if (diff != 0) {
        tjei_calculate_variable_length_int(diff, vli); // 计算变长整数
        // 使用哈夫曼编码写入位数
        tjei_write_bits(state, bitbuffer, location, huff_dc_len[vli[1]], huff_dc_code[vli[1]]);
        // 写入位
        tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]);
    } else {
        tjei_write_bits(state, bitbuffer, location, huff_dc_len[0], huff_dc_code[0]); // 写入零差值
    }

    // ==== 编码AC系数 ====
    int last_non_zero_i = 0;
    // 找到最后一个非零元素
    for (int i = 63; i > 0; --i) {
        if (du[i] != 0) {
            last_non_zero_i = i;
            break;
        }
    }

    for (int i = 1; i <= last_non_zero_i; ++i) {
        // 如果为零，增加计数。如果>=15，编码(FF,00)
        int zero_count = 0;
        while (du[i] == 0) {
            ++zero_count;
            ++i;
            if (zero_count == 16) {
                // 编码(ff,00) == 0xf0
                tjei_write_bits(state, bitbuffer, location, huff_ac_len[0xf0], huff_ac_code[0xf0]);
                zero_count = 0;
            }
        }
        tjei_calculate_variable_length_int(du[i], vli); // 计算变长整数

        assert(zero_count < 0x10);
        assert(vli[1] <= 10);

        uint16_t sym1 = (uint16_t)((uint16_t)zero_count << 4) | vli[1]; // 组合零计数和大小

        assert(huff_ac_len[sym1] != 0);

        // 写入符号1 --- (游程长度, 大小)
        tjei_write_bits(state, bitbuffer, location, huff_ac_len[sym1], huff_ac_code[sym1]);
        // 写入符号2 --- (幅度)
        tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]);
    }

    if (last_non_zero_i != 63) {
        // 写入EOB HUFF(00,00)
        tjei_write_bits(state, bitbuffer, location, huff_ac_len[0], huff_ac_code[0]);
    }
    return;
}

// 哈夫曼表类型枚举
enum {
    TJEI_LUMA_DC,   // 亮度DC
    TJEI_LUMA_AC,   // 亮度AC
    TJEI_CHROMA_DC, // 色度DC
    TJEI_CHROMA_AC, // 色度AC
};

// 处理后的量化表结构（仅用于快速DCT）
#if TJE_USE_FAST_DCT
struct TJEProcessedQT {
    float chroma[64]; // 色度量化表
    float luma[64];   // 亮度量化表
};
#endif

// 在状态中设置哈夫曼表
static void tjei_huff_expand(TJEState *state)
{
    assert(state);

    // 设置哈夫曼位数表
    state->ht_bits[TJEI_LUMA_DC] = tjei_default_ht_luma_dc_len;
    state->ht_bits[TJEI_LUMA_AC] = tjei_default_ht_luma_ac_len;
    state->ht_bits[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc_len;
    state->ht_bits[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac_len;

    // 设置哈夫曼值表
    state->ht_vals[TJEI_LUMA_DC] = tjei_default_ht_luma_dc;
    state->ht_vals[TJEI_LUMA_AC] = tjei_default_ht_luma_ac;
    state->ht_vals[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc;
    state->ht_vals[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac;

    // 计算每个表的总代码数
    int32_t spec_tables_len[4] = { 0 };
    for (int i = 0; i < 4; ++i) {
        for (int k = 0; k < 16; ++k) {
            spec_tables_len[i] += state->ht_bits[i][k];
        }
    }

    // 填充扩展表
    uint8_t huffsize[4][257];
    uint16_t huffcode[4][256];
    for (int i = 0; i < 4; ++i) {
        assert(256 >= spec_tables_len[i]);
        tjei_huff_get_code_lengths(huffsize[i], state->ht_bits[i]);        // 获取代码长度
        tjei_huff_get_codes(huffcode[i], huffsize[i], spec_tables_len[i]); // 获取代码
    }
    for (int i = 0; i < 4; ++i) {
        int64_t count = spec_tables_len[i];
        tjei_huff_get_extended(state->ehuffsize[i],
                               state->ehuffcode[i],
                               state->ht_vals[i],
                               &huffsize[i][0],
                               &huffcode[i][0], count); // 获取扩展表
    }
}

// 主编码函数
static int tjei_encode_main(TJEState *state,
                            const unsigned char *src_data,
                            const int width,
                            const int height,
                            const int src_num_components)
{
    // 检查输入参数有效性
    if (src_num_components != 3 && src_num_components != 4) {
        return 0;
    }

    if (width > 0xffff || height > 0xffff) {
        return 0;
    }

// 预处理量化表（仅用于快速DCT）
#if TJE_USE_FAST_DCT
    struct TJEProcessedQT pqt;
    // 来自经典日本实现
    /* 对于浮点AA&N IDCT方法，除数等于量化系数乘以缩放因子[row]*缩放因子[col]，
     *   缩放因子[0] = 1
     *   缩放因子[k] = cos(k*PI/16) * sqrt(2)    for k=1..7
     * 我们应用额外的缩放因子8。
     * 实际存储的是1/除数，以便内循环可以使用乘法而不是除法。
     */
    static const float aan_scales[] = {
        1.0f, 1.387039845f, 1.306562965f, 1.175875602f,
        1.0f, 0.785694958f, 0.541196100f, 0.275899379f
    };

    // 构建（反）量化表
    for (int y = 0; y < 8; y++) {
        for (int x = 0; x < 8; x++) {
            int i = y * 8 + x;
            pqt.luma[y * 8 + x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_luma[tjei_zig_zag[i]]);
            pqt.chroma[y * 8 + x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_chroma[tjei_zig_zag[i]]);
        }
    }
#endif

    { // 写入文件头
        TJEJPEGHeader header;
        // JFIF头
        header.SOI = tjei_be_word(0xffd8); // 顺序DCT
        header.APP0 = tjei_be_word(0xffe0);

        uint16_t jfif_len = sizeof(TJEJPEGHeader) - 4 /*SOI & APP0标记*/;
        header.jfif_len = tjei_be_word(jfif_len);
        memcpy(header.jfif_id, (void *)tjeik_jfif_id, 5);
        header.version = tjei_be_word(0x0102);
        header.units = 0x01;                     // 每英寸点数
        header.x_density = tjei_be_word(0x0060); // 96 DPI
        header.y_density = tjei_be_word(0x0060); // 96 DPI
        header.x_thumb = 0;
        header.y_thumb = 0;
        tjei_write(state, &header, sizeof(TJEJPEGHeader), 1);
    }
    { // 写入注释
        TJEJPEGComment com;
        uint16_t com_len = 2 + sizeof(tjeik_com_str) - 1;
        // 注释
        com.com = tjei_be_word(0xfffe);
        com.com_len = tjei_be_word(com_len);
        memcpy(com.com_str, (void *)tjeik_com_str, sizeof(tjeik_com_str) - 1);
        tjei_write(state, &com, sizeof(TJEJPEGComment), 1);
    }

    // 写入量化表
    tjei_write_DQT(state, state->qt_luma, 0x00);   // 亮度量化表
    tjei_write_DQT(state, state->qt_chroma, 0x01); // 色度量化表

    { // 写入帧标记
        TJEFrameHeader header;
        header.SOF = tjei_be_word(0xffc0);
        header.len = tjei_be_word(8 + 3 * 3);
        header.precision = 8;
        assert(width <= 0xffff);
        assert(height <= 0xffff);
        header.width = tjei_be_word((uint16_t)width);
        header.height = tjei_be_word((uint16_t)height);
        header.num_components = 3;
        uint8_t tables[3] = {
            0, // 亮度组件使用亮度表
            1, // 色度组件使用色度表
            1, // 色度组件使用色度表
        };
        for (int i = 0; i < 3; ++i) {
            TJEComponentSpec spec;
            spec.component_id = (uint8_t)(i + 1);  // 组件ID（1,2,3）
            spec.sampling_factors = (uint8_t)0x11; // 采样因子（1:1）
            spec.qt = tables[i];                   // 量化表选择器

            header.component_spec[i] = spec;
        }
        // 写入文件
        tjei_write(state, &header, sizeof(TJEFrameHeader), 1);
    }

    // 写入哈夫曼表
    tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_DC], state->ht_vals[TJEI_LUMA_DC], TJEI_DC, 0);
    tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_AC], state->ht_vals[TJEI_LUMA_AC], TJEI_AC, 0);
    tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_DC], state->ht_vals[TJEI_CHROMA_DC], TJEI_DC, 1);
    tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_AC], state->ht_vals[TJEI_CHROMA_AC], TJEI_AC, 1);

    // 写入扫描开始
    {
        TJEScanHeader header;
        header.SOS = tjei_be_word(0xffda);
        header.len = tjei_be_word((uint16_t)(6 + (sizeof(TJEFrameComponentSpec) * 3)));
        header.num_components = 3;

        uint8_t tables[3] = {
            0x00, // 亮度DC/AC表
            0x11, // 色度DC/AC表
            0x11, // 色度DC/AC表
        };
        for (int i = 0; i < 3; ++i) {
            TJEFrameComponentSpec cs;
            // 必须与上面帧头中的component_id相同
            cs.component_id = (uint8_t)(i + 1);
            cs.dc_ac = (uint8_t)tables[i]; // DC/AC表选择器

            header.component_spec[i] = cs;
        }
        header.first = 0; // 第一个DCT系数
        header.last = 63; // 最后一个DCT系数
        header.ah_al = 0; // 渐进参数
        tjei_write(state, &header, sizeof(TJEScanHeader), 1);
    }

    // 写入压缩数据
    float du_y[64]; // 亮度数据单元
    float du_b[64]; // 蓝色差数据单元
    float du_r[64]; // 红色差数据单元

    // 将差值初始化为0
    int pred_y = 0; // 亮度DC预测值
    int pred_b = 0; // 蓝色差DC预测值
    int pred_r = 0; // 红色差DC预测值

    // 位堆栈
    uint32_t bitbuffer = 0; // 位缓冲区
    uint32_t location = 0;  // 位位置

    // 遍历图像中的每个8x8块
    for (int y = 0; y < height; y += 8) {
        for (int x = 0; x < width; x += 8) {
            // 块循环：====
            for (int off_y = 0; off_y < 8; ++off_y) {
                for (int off_x = 0; off_x < 8; ++off_x) {
                    int block_index = (off_y * 8 + off_x); // 块内索引

                    int src_index = (((y + off_y) * width) + (x + off_x)) * src_num_components; // 源数据索引

                    int col = x + off_x;
                    int row = y + off_y;

                    // 处理边界情况
                    if (row >= height) {
                        src_index -= (width * (row - height + 1)) * src_num_components;
                    }
                    if (col >= width) {
                        src_index -= (col - width + 1) * src_num_components;
                    }
                    assert(src_index < width * height * src_num_components);

                    // 获取RGB值
                    uint8_t r = src_data[src_index + 0];
                    uint8_t g = src_data[src_index + 1];
                    uint8_t b = src_data[src_index + 2];

                    // 转换为YCbCr颜色空间
                    float luma = 0.299f * r + 0.587f * g + 0.114f * b - 128; // Y分量
                    float cb = -0.1687f * r - 0.3313f * g + 0.5f * b;        // Cb分量
                    float cr = 0.5f * r - 0.4187f * g - 0.0813f * b;         // Cr分量

                    du_y[block_index] = luma;
                    du_b[block_index] = cb;
                    du_r[block_index] = cr;
                }
            }

            // 编码并写入亮度MCU
            tjei_encode_and_write_MCU(state, du_y,
#if TJE_USE_FAST_DCT
                                      pqt.luma,
#else
                                      state->qt_luma,
#endif
                                      state->ehuffsize[TJEI_LUMA_DC], state->ehuffcode[TJEI_LUMA_DC],
                                      state->ehuffsize[TJEI_LUMA_AC], state->ehuffcode[TJEI_LUMA_AC],
                                      &pred_y, &bitbuffer, &location);

            // 编码并写入蓝色差MCU
            tjei_encode_and_write_MCU(state, du_b,
#if TJE_USE_FAST_DCT
                                      pqt.chroma,
#else
                                      state->qt_chroma,
#endif
                                      state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC],
                                      state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC],
                                      &pred_b, &bitbuffer, &location);

            // 编码并写入红色差MCU
            tjei_encode_and_write_MCU(state, du_r,
#if TJE_USE_FAST_DCT
                                      pqt.chroma,
#else
                                      state->qt_chroma,
#endif
                                      state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC],
                                      state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC],
                                      &pred_r, &bitbuffer, &location);
        }
    }

    // 完成图像
    { // 刷新
        if (location > 0 && location < 8) {
            tjei_write_bits(state, &bitbuffer, &location, (uint16_t)(8 - location), 0); // 填充剩余位
        }
    }
    uint16_t EOI = tjei_be_word(0xffd9); // 结束标记
    tjei_write(state, &EOI, sizeof(uint16_t), 1);

    // 刷新输出缓冲区
    if (state->output_buffer_count) {
        state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count);
        state->output_buffer_count = 0;
    }

    return 1;
}

// 编码到文件（默认质量）
int tje_encode_to_file(const char *dest_path,
                       const int width,
                       const int height,
                       const int num_components,
                       const unsigned char *src_data)
{
    int res = tje_encode_to_file_at_quality(dest_path, 3, width, height, num_components, src_data);
    return res;
}

// 标准库写入函数
static void tjei_stdlib_func(void *context, void *data, int size)
{
    FILE* fd = (FILE*)context;
    fwrite(data, size, 1, fd);
    // lv_fs_file_t *f = (lv_fs_file_t *)context;
    // ;
    // lv_fs_write(f, data, size, NULL); // 使用LVGL文件系统写入
}

// 公共接口：以指定质量编码到文件
int tje_encode_to_file_at_quality(const char *dest_path,
                                  const int quality,
                                  const int width,
                                  const int height,
                                  const int num_components,
                                  const unsigned char *src_data)
{
    ESP_LOGE("debug", "dest_path: %s", dest_path);
    // lv_fs_res_t res;
    //lv_fs_file_t f;
    FILE *f = NULL;
    f = fopen(dest_path, "wb");
    if(f == NULL)
        return 0;
    // res = lv_fs_open(&f, dest_path, LV_FS_MODE_WR); // 打开文件
    // if (res != LV_FS_RES_OK)
    //     return 0;

    int result = tje_encode_with_func(tjei_stdlib_func, f,
                                      quality, width, height, num_components, src_data); // 编码

    result |= 0 == fclose(f); //lv_fs_close(&f); // 关闭文件

    return result;
}

// 使用自定义写入函数编码
int tje_encode_with_func(tje_write_func *func,
                         void *context,
                         const int quality,
                         const int width,
                         const int height,
                         const int num_components,
                         const unsigned char *src_data)
{
    // 检查质量参数有效性
    if (quality < 1 || quality > 3) {
        //tje_log("[ERROR] -- 有效的'quality'值为1(最低), 2, 或3(最高)\n");
        return 0;
    }

    TJEState state = { 0 }; // 初始化状态

    uint8_t qt_factor = 1;
    switch (quality) {
        case 3: // 最高质量
            for (int i = 0; i < 64; ++i) {
                state.qt_luma[i] = 1;   // 亮度量化表全1
                state.qt_chroma[i] = 1; // 色度量化表全1
            }
            break;
        case 2: // 高质量
            qt_factor = 10;
            // 不break，继续执行case 1
        case 1: // 标准质量
            for (int i = 0; i < 64; ++i) {
                state.qt_luma[i] = tjei_default_qt_luma_from_spec[i] / qt_factor; // 缩放亮度量化表
                if (state.qt_luma[i] == 0) {
                    state.qt_luma[i] = 1; // 确保不为零
                }
                state.qt_chroma[i] = tjei_default_qt_chroma_from_paper[i] / qt_factor; // 缩放色度量化表
                if (state.qt_chroma[i] == 0) {
                    state.qt_chroma[i] = 1; // 确保不为零
                }
            }
            break;
        default:
            assert(!"无效代码路径");
            break;
    }

    TJEWriteContext wc = { 0 };
    wc.context = context;
    wc.func = func;
    state.write_context = wc; // 设置写入上下文

    tjei_huff_expand(&state); // 扩展哈夫曼表

    int result = tjei_encode_main(&state, src_data, width, height, num_components); // 主编码过程

    return result;
}

// ============================================================
#endif // TJE_IMPLEMENTATION
// ============================================================

// 恢复GCC/Clang诊断状态
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif


// 结束C链接约定
#ifdef __cplusplus
} // extern "C"
#endif